package com.medord.machine.core.aspect;

import com.medord.machine.core.FlowState;
import com.medord.machine.core.annotition.FlowAspectAnno;
import com.medord.machine.core.entity.ICurrentStateDto;
import com.medord.machine.utils.FlowSpringContextHolder;
import lombok.SneakyThrows;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

/**
 * @author 孙丰佳
 * @version V1.0
 * @Title: FlowAspect
 * @Package com.medord.machine.core.aspect
 * @Description: (用一句话描述该文件做什么)
 * @date 2021-09-17 9:22
 **/
@Aspect
@Component
public class FlowAspect {
    private static final Logger logger = LoggerFactory.getLogger(FlowAspect.class);
    private FlowState target;


    @Pointcut("@annotation(com.medord.machine.core.annotition.FlowAspectAnno)" +
            " && args(iCurrentStateDto, ..)")
    public void BrokerAspect(ICurrentStateDto iCurrentStateDto) {}

    /**
     * 切面主逻辑：
     * 1、获取切面前置、后置处理类
     * 2、获取被代理类，可以在被代理类中获取入参，过程参数，加warnning信息等操作
     * 3、执行前置处理
     * 4、执行被代理类方法
     * 5、执行后置处理
     * 整个逻辑中如果需要warnning信息，可以直接加，需要报错，需要抛异常
     * @param joinPoint
     * @param iCurrentStateDto
     * @param anno
     * @return java.lang.Object
     * @author 孙丰佳
     * @time 2021-09-17 11:10
     */
    @Around(value = "BrokerAspect(iCurrentStateDto) && @annotation(anno)")
    public Object doAround(ProceedingJoinPoint joinPoint, ICurrentStateDto iCurrentStateDto, FlowAspectAnno anno) {
        Class[] prefixCheckNames = anno.prefixCheckNames();
        Class[] suffixCheckNames = anno.suffixCheckNames();

        try {
            this.target = (FlowState) joinPoint.getTarget();
        } catch (Exception e) {
            new RuntimeException("无法获取代理类，需要代理FlowState类型的类！");
        }

        for (Class clazz : prefixCheckNames) {
            doRun(clazz, iCurrentStateDto);
        }

        Object result;
        try {
            result = joinPoint.proceed();
        } catch (Throwable throwable) {
            throw new RuntimeException("【" + iCurrentStateDto.getName() + "】状态变化过程发生错误，请联系管理员！");
        }


        for (Class clazz : suffixCheckNames) {
            doRun(clazz, iCurrentStateDto);
        }
        //返回值逻辑
        return result;
    }

    @SneakyThrows
    private void doRun(Class beanClass, ICurrentStateDto iCurrentStateDto) {
        final BaseFlowHandler handler;
        try {
            handler = (BaseFlowHandler) FlowSpringContextHolder.getBean(beanClass);
        } catch (Exception e) {
            logger.error("【" + beanClass.getName() + "】" + "类无法生成对象", e);
            throw new RuntimeException("【" + beanClass.getName() + "】" + "类无法生成对象");
        }

        if (ObjectUtils.isEmpty(handler)) {
            throw new RuntimeException("【" + beanClass.getName() + "】" + "类无法生成对象");
        }

        if (!handler.needExecute(iCurrentStateDto)) {
            return;
        }

        try {
            handler.run(iCurrentStateDto);
        } catch (Exception e) {
            if (handler.needThrowException()) {
                throw new RuntimeException(e.getMessage());
            }
            this.target.addWarningMsg("【" + iCurrentStateDto.getName() + "】" + "变更过程出现错误：" + e.getMessage());
        }

    }
}
